import utils from './utils';
import { danmakuType } from './danmakuType';
import danmaku, { position } from './danmaku';

export default class ssSubtitle {
  private danmaku: danmaku;
  private bottomSubtitles: Map<number, number>;
  private topSubtitles: Map<number, number>;
  private videoWidth: number;
  private videoHeight: number;
  private baseFontSize: number;
  private lineCount: number;
  private bottomMargin: number;
  private tuneSeconds: number;
  private textLength: number;
  private startTime: number;
  private endTime: number;
  private fontSize: number;
  private position: position;
  private styledText: string;

  constructor(danmaku: danmaku, bottomSubtitles: Map<number, number>, topSubtitles: Map<number, number>, videoWidth: number, videoHeight: number, baseFontSize: number,
    lineCount: number, bottomMargin: number, tuneSeconds: number) {
    this.danmaku = danmaku;
    this.bottomSubtitles = bottomSubtitles;
    this.topSubtitles = topSubtitles;
    this.videoWidth = videoWidth;
    this.videoHeight = videoHeight;
    this.baseFontSize = baseFontSize;
    this.lineCount = lineCount;
    this.bottomMargin = bottomMargin;
    this.tuneSeconds = tuneSeconds;
    this.textLength = danmaku.content.length;
    this.startTime = danmaku.showTime;
    this.endTime = this.getEndTime(danmaku.showTime, this.tuneSeconds);
    this.fontSize = danmaku.size - 25 + baseFontSize;
    this.position = this.getPosition();
    this.styledText = this.getStyledText();
  }

  toString() {
    var start = this.format(utils.formatTime(this.startTime));
    var end = this.format(utils.formatTime(this.endTime));
    return `Dialogue: 3,${start},${end},AcplayDefault,,0000,0000,0000,,${this.styledText}`;
  }

  private format(s: string): string {
    if (!s.includes('.'))
      return s += ".00";
    if (s.substring(s.lastIndexOf('.')).length == 2)
      return s + "0";
    if (s.substring(s.lastIndexOf('.')).length > 3) {
      var index = s.lastIndexOf('.');
      return s.slice(index + 3, s.length - index - 3);
    }
    return s;
  }

  private getStyledText() {
    let colourMarkup, borderMarkup, fontSizeMarkup, styleMarkup;
    if (this.danmaku.colour == 16777215) colourMarkup = "";
    else colourMarkup = `\\c&H${utils.toHexString(this.danmaku.colour)}`;
    if (this.needWhiteBorder(this.danmaku)) borderMarkup = "\\3c&HFFFFFF";
    else borderMarkup = "";
    if (this.fontSize == this.baseFontSize) fontSizeMarkup = "";
    else fontSizeMarkup = `\\fs${this.fontSize}`;
    if (this.danmaku.type == danmakuType.normal || this.danmaku.type == danmakuType.normal2
      || this.danmaku.type == danmakuType.normal3 || this.danmaku.type == danmakuType.reverse)
      styleMarkup = `\\move(${this.position.x1},${this.position.y1},${this.position.x2},${this.position.y2})`;
    else
      styleMarkup = `\\a6\\pos(${this.position.x1},${this.position.y1})`;
    return `{{${styleMarkup}${colourMarkup}${borderMarkup}${fontSizeMarkup}}}${this.danmaku.content}`;
  }

  private needWhiteBorder(danmaku: danmaku): boolean {
    var colour = danmaku.colour;
    if (colour == 0) return true;
    var hls = utils.toHls(utils.toRgb(danmaku.colour));
    if (hls.h > 30 && hls.h < 210 && hls.l < 33) return true;
    if ((hls.l < 30 || hls.h > 210) && hls.l < 66) return true;
    return false;
  }

  private getPosition(): position {
    var position = new position();
    if (this.danmaku.type == danmakuType.normal || this.danmaku.type == danmakuType.normal2 || this.danmaku.type == danmakuType.normal3) {
      position.X1 = this.videoWidth + this.baseFontSize * this.textLength / 2;
      position.X2 = -(this.baseFontSize * this.textLength) / 2;
      let y = (this.danmaku.index % this.lineCount + 1) * this.baseFontSize;
      if (y < this.fontSize) y = this.fontSize;
      position.Y1 = position.Y2 = y;
    }
    else if (this.danmaku.type == danmakuType.bottom) {
      var lineIndex = this.chooseLineCount(this.bottomSubtitles, this.startTime);
      if (this.bottomSubtitles.has(lineIndex))
        this.bottomSubtitles[lineIndex] = this.endTime;
      else
        this.bottomSubtitles.set(lineIndex, this.endTime);
      let x = this.videoWidth / 2;
      let y = this.videoHeight - (this.baseFontSize * lineIndex + this.bottomMargin);
      position.X1 = position.X2 = x;
      position.Y1 = position.Y2 = y;
    }
    else if (this.danmaku.type == danmakuType.top) {
      var lineIndex = this.chooseLineCount(this.topSubtitles, this.startTime);
      if (this.topSubtitles.has(lineIndex))
        this.topSubtitles[lineIndex] = this.endTime;
      else
        this.topSubtitles.set(lineIndex, this.endTime);
      let x = this.videoWidth / 2;
      let y = this.baseFontSize * lineIndex + 1;
      position.X1 = position.X2 = x;
      position.Y1 = position.Y2 = y;
    }
    else if (this.danmaku.type == danmakuType.reverse) {
      position.X2 = this.videoWidth + this.baseFontSize * this.textLength / 2;
      position.X1 = -(this.baseFontSize * this.textLength) / 2;
      let y = (this.danmaku.index % this.lineCount + 1) * this.baseFontSize;
      if (y < this.fontSize) y = this.fontSize;
      position.Y1 = position.Y2 = y;
    }
    return position;
  }

  private chooseLineCount(subtitles: Map<number, number>, startTime: number) {
    let lineIndex: number;
    const keys: number[] = [...subtitles.keys()];
    var toRemove: Array<number> = [];
    for (var item of subtitles) {
      if (item[1] <= startTime)
        toRemove.push(item[0]);
    }
    toRemove.forEach(s => subtitles.delete(s));
    if (subtitles.size == 0)
      lineIndex = 0;
    else if (subtitles.size == Math.max(...keys))
      lineIndex = Math.min(...keys);
    else {
      lineIndex = 0;
      for (let i = 0; i < subtitles.size; i++) {
        if (!subtitles.has(i)) {
          lineIndex = i;
          break;
        }
      }
      if (lineIndex == 0)
        lineIndex = subtitles.size;
    }
    return lineIndex;
  }

  private getEndTime(showTime: number, offset: number): number {
    if (this.danmaku.type == danmakuType.bottom || this.danmaku.type == danmakuType.top)
      return showTime + 4;
    let endTime;
    if (this.textLength < 5)
      endTime = showTime + 7 + this.textLength / 1.5;
    else if (this.textLength < 12)
      endTime = showTime + 7 + this.textLength / 2;
    else
      endTime = showTime + 13;
    endTime += offset;
    return endTime;
  }
}